>Folks, I went through exactly the same thing when I developed the LPRng >print spooler package. The solution that I used for the problem was as >follows: > >1. In the startup code, use seteuid()/setreud() to set > EUID to something banal such as daemon, and RUID to root. > (you might want to save the original RUID for permissions checking). The correct approach, though OS milage may vary. On systems with saved user ids, it's usually best *not* to set the ruid to another uid than the originally invoking userid and start you program of with: uid_t euid; main ... { euid = geteuid(); seteuid(getuid()); when you require the set-uid powers you can then do: seteuid(euid); /* section requiring special powers; bind to reserved port, open /etc/shadow */ seteuid(getuid()); (On some systems use setreuid(-1, euid); ... setreuid(-1, getuid());) Again. this requires a saved uid mechanism (available in SVR4, SunOS 4 and countless others) You don't need to specifically reset the uids for an exec either as the effective user id and real user id are always the invoking (or non-dangerous) uid. Coding like this also serve another useful purpose; the casual user of the code can immediately see where the program requires its special powers and why it needs to be set-uid in the first place. When you're no longer in need of having the powers at all you may think of giving them up completely. First of all, this is not always possible. (SVR4 doesn't allow you to do away the saved uid unless you're root) But *after* you've given away all powers (setreuid(getuid(),getuid()) or setuid(getuid())), you're vulnerable to a debugger attaching to the process. So if you totally give up all powers you need to close all the special fds you had and destroy any special data (/etc/shadow). It's often better to just keep the saved uid and not having to worry about this. Removing read access from the executable usually also prevents the attack through debuggers. After an exec, the saved uid will be gone anyway. >2. Do all operations EXCEPT socket() and bind() calls as EUID daemon. > It turns out that on some ^&*(*&*( systems when you want to bind > to a reserved port, you must open the socket EUID ROOT and to the > bind EUID root. SVR4 systes often have that problem. >3. Before you do an exec, do a seteuid/setuid > to the original user and/or daemon UID (your application milage may > vary on this). With the saved set-uid model, you are running with euid==uid most of the time and this step isn't necessary. Obviously, special care ust be taken with signal handlers (don't know the state of the uid) and in multithreaded apps (don't have multithreaded set-uid apps). Even implied multithreading (e.g., RPC servers with threads) is dangerous if uid juggling is necessary. Casper